Ana içeriğe geç
  1. 100 Günde SwiftUI Notları/

8.Gün - Swift Fonksiyonlar - 2 : Varsayılan Değer ve Error Handling

Fonksiyon Parametreleri için Varsayılan Değer #

Fonksiyonlarımızı parametreler vasıtasıyla özelleştirebildiğimizden bahsetmiştik. Parametrelerimize varsayılan değer sağlayarak bu özelleştirmeyi daha da opsiyonel bir hale getirebiliriz.

Daha önceki dersimizde yazdığımız bir fonksiyonu inceleyelim;

func printTimesTables(for number: Int, end: Int) {
    for i in 1...end {
        print("\(i) x \(number) is \(i * number)")
    }
}

printTimesTables(for: 5, end: 20)

Yukarıdaki kod, 1’den başlayarak herhangi bir noktaya kadar istediğimiz sayının çarpım tablosunu vermektedir. Son nokta her zaman değişken olabilir fakat çoğu zaman 10 veya 12 olmasını isteriz. Fakat bazen de bizim belirlediğimiz bir son noktaya da gitmesi gerekebilir.

Bu problemi çözebilmek için, Swift fonksiyon parametrelerine varsayılan değer atamamıza izin verir. Aşağıdaki kodda end parametresi için 12 değeri varsayılan değer olarak atanmıştır. Biz fonksiyon çağrısı esnasında aksini belirtmezsek, 12 değeri kullanılacaktır.

func printTimesTables(for number: Int, end: Int = 12) {
    for i in 1...end {
        print("\(i) x \(number) is \(i * number)")
    }
}

printTimesTables(for: 5, end: 20)
printTimesTables(for: 8)

printTimesTables() fonksiyonunu iki farklı şekilde çağırabiliyoruz. İstersek iki parametreyi (for: end:) birden sağlarız, yada tek bir parametre (for:) sağlar diğer parametrenin varsayılan değerini kullanırız.

Swift optimizasyon için, Array öğelerini tutmaya yetecek alandan biraz fazlasını ayırır, Array’in boyutu arttıkça ayrılan alanı da arttırır. Böylece mümkün olduğunca az bellek harcar.

var characters = ["Lana", "Pam", "Ray", "Sterling"]
print(characters.count)
characters.removeAll()
print(characters.count)

removeAll() fonksiyonu çağrıldığında, Swift otomatik olarak Array’deki tüm öğeleri kaldıracak ve ayrılmış belleği boşaltacaktır. Fakat bazı durumlarda Array öğelerini kaldırmak fakat ayrılan belleği de tutmak isteyebiliriz. (Bir Array’deki tüm öğeleri kaldırıp, çok sayıda yeni öğe eklemek isteyebiliriz). İşte removeAll() fonksiyonunun, öğeleri kaldırıp bellek alanını koruduğu bir işlevi de vardır.

characters.removeAll(keepingCapacity: true)

removeAll() fonksiyonunun bu davranışı, fonksiyonların varsayılan parametre özelliği sayesinde gerçekleşir. keepingCapacity parametresinin varsayılan değeri false tır. Bu sayede hem öğeleri kaldırır hem de bellek alanını temizler. (Biz fonksiyon çağrısı esnasında özellikle belirtmediğimiz sürece false olarak kalmaya devam eder.) Array’in bellek alanını korumak istediğimizde ise bu parametreyi true olarak değiştiririz.

Fonksiyonlarda Error Handling #

Okumak istediğimiz dosyanın olmaması, indirmeye çalıştığımız verinin, internet bağlantısı kesildiği için başarısız olması gibi durumlar her zaman meydana gelebilir. Bu hataları düzgünce işlemezsek, kodumuz çökebilir. Bu sebeple, fonksiyonlarda meydana gelebilecek hatalara göre hangi davranışlarda bulunacağımızı belirlemeliyiz.

Error Handling üç adımdan oluşur;

  1. Swift’e, meydana gelebilecek olası hatalar için bilgi vermek.
  2. Hata oluştuğunda bunu haber verebilecek bir fonksiyon yazmak.
  3. Fonksiyonun çağrılması ve oluşabilecek hataların ele alınması.

Kullanıcının şifresini kontrol eden bir fonksiyon yazalım, şifre çok kısaysa veya çok kolaysa bir hata oluşturalım.

Swift’e, meydana gelebilecek olası hatalar için bilgi vermek. #

İşe, meydana gelebilecek olası hataları tanımlayarak başlayalım. Bu da Error tipini temel alan bir enum oluşturmamızı gerektirmektedir.

enum PasswordError: Error {
    case short, obvious
}

Yukarıdaki enum, iki olası hata durumu olduğunu söylüyor : short ve obvious. Bu enum ne anlama geldiğini tanımlamıyor, sadece bu durumların var olduğunu söylüyor.

Hata oluştuğunda bunu haber verebilecek bir fonksiyon yazmak. #

İkinci adımımızda bu hataları tetikleyebilecek bir fonksiyon yazacağız. Bir hata tetiklendiğinde, fonksiyon normal bir şekilde devam etmek yerine, değer geri döndürmeden hemen sonlanacaktır.

func checkPassword(_ password: String) throws -> String {
    if password.count < 5 {
        throw PasswordError.short
    }

    if password == "12345" {
        throw PasswordError.obvious
    }

    if password.count < 8 {
        return "OK"
    } else if password.count < 10 {
        return "Good"
    } else {
        return "Excellent"
    }
}

Yukarıdaki kodu biraz inceleyelim;

  • Bir fonksiyon, hataları kendisi işlemeden fırlatabiliyorsa, fonksiyonun geri dönüş türünden önce throws olarak işaretlenir.
  • Fonksiyon tarafından tam olarak ne tür bir hata fırlatılacağını belirtmiyoruz, sadece hata fırlatılabileceğini söylüyoruz.
  • Fonksiyonun throw ile işaretlenmiş olması, mutlaka hata fırlatacağı anlamına gelmez, sadece olabileceğini belirtir.
  • Bir hata meydana geldiğinde, throw ve PasswordError case’lerinden birini yazıyoruz. Bu fonksiyondan hemen çıkar, yani bir String döndürmez.
  • Eğer bir hata fırlatılmazsa, fonksiyon normal bir şekilde davranır yani bir String döndürür.

Böylelikle hataların ele alınmasındaki ikinci adımı tamamlamış olduk.

Fonksiyonun çağrılması ve oluşabilecek hataların ele alınması. #

Bu adım üç alt adımdan oluşmaktadır.

  1. Hata verebilecek bir kod bloğunu do ile başlatmak,
  2. try kullanarak bir veya daha fazla throw fonksiyonu çağırmak,
  3. Fırlatılan hataları catch kullanarak işlemek

Örneğimizden devam edelim;

let string = "12345"

do {
    let result = try checkPassword(string)
    print("Password rating: \(result)")
} catch {
    print("There was an error.")
}

//ÇIKTI:
//----------------------------------------
//There was an error.

checkPassword() fonksiyonu hatasız olarak çalışırsa bir değer döndürür ve bu değer result sabitine atanır. Ancak bir hata meydana gelirse (yukarıdaki koda göre oluşacaktır) “Password rating:” kısmı terminale yazdırılmayacak, program akışı catch bloğuna yönlenecektir.

try , hata verebilecek tüm fonksiyonlar çağrılmadan önce yazılmalıdır, bu geliştiricilere bir hata oluşması durumunda normal kod akışının kesintiye uğrayacağına dair görsel bir işarettir.

do {
    try throwingFunction1()
    nonThrowingFunction1()
    try throwingFunction2()
    nonThrowingFunction2()
    try throwingFunction3()
} catch {
    // handle errors
}

try ’ı kullanabilmek için mutlaka bir do bloğu içinde olmamız ve herhangi bir hatayı ele alabilecek bir veya daha fazla catch bloğuna sahip olmamız gerekir. Çok nadir de olsa, fonksiyonun hata fırlatmayacağından kesinlikle emin olduğumuz durumda try! şeklinde bir kullanım yapabiliriz. try! kullanıldığında do ve catch bloklarının kullanımına gerek yoktur.

catch bloğu ile belirli hataları da yakalayabiliriz. Ama mutlaka her hatanın yakalandığına emin olmalıyız.

let string = "12345"

do {
    let result = try checkPassword(string)
    print("Password rating: \(result)")
} catch PasswordError.short {
    print("Please use a longer password.")
} catch PasswordError.obvious {
    print("I have the same combination on my luggage!")
} catch {
    print("There was an error.")
}
//ÇIKTI:
//----------------------------------------
//I have the same combination on my luggage!

Bu yazıyı İngilizce olarak da okuyabilirsiniz.
You can also read this article in English.

Bu yazı, SwiftUI Day 8 adresinde bulunan yazılardan kendim için aldığım notları içermektedir. Orjinal dersi takip etmek için lütfen bağlantıya tıklayın.